home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / ListMania / ListMania.c next >
Encoding:
Text File  |  2000-09-28  |  11.2 KB  |  375 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        ListMania.c
  3.  
  4.     Contains:    Sample for demonstrating use of OT list utilities.
  5.  
  6.     Written by: Quinn "The Eskimo!"    
  7.  
  8.     Copyright:    Copyright © 1997-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 7/22/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23. /////////////////////////////////////////////////////////////////////
  24. // The OT debugging macros in <OTDebug.h> require this variable to
  25. // be set.
  26.  
  27. #ifndef qDebug
  28. #define qDebug    1
  29. #endif
  30.  
  31. /////////////////////////////////////////////////////////////////////
  32. // Pick up all the standard OT stuff.
  33.  
  34. #include <OpenTransport.h>
  35.  
  36. /////////////////////////////////////////////////////////////////////
  37. // Pick up the OTDebugBreak and OTAssert macros.
  38.  
  39. #include <OTDebug.h>
  40.  
  41. /////////////////////////////////////////////////////////////////////
  42. // Standard C prototypes.
  43.  
  44. #include <stdio.h>
  45.  
  46. /////////////////////////////////////////////////////////////////////
  47. // OTDebugStr is not defined in any OT header files, but it is
  48. // exported by the libraries, so we define the prototype here.
  49.  
  50. extern pascal void OTDebugStr(const char* str);
  51.  
  52. /////////////////////////////////////////////////////////////////////
  53.  
  54. // Notes
  55. // -----
  56. // This sample is designed to illustrate the use of the OT link-list
  57. // routines in a simple producer/consumer environment.  The objects
  58. // being produced and consumed are widgets, as defined by the Widget
  59. // data type.  There are two key routines: ProduceWidgets and
  60. // ConsumeWidgets.  The first routine produces widgets (either by
  61. // reusing previously consumed (ie free) widgets, or by creating new
  62. // ones) and puts them on a 'pending' list.  The second consumes the
  63. // widgets by grabbing them off the pending list.  After consuming
  64. // the widget (in this sample, "consumption" means merely printing
  65. // to stdout), the routine returns the widget to a free list.
  66. //
  67. // This sample uses OTLIFO routines throughout.  OTLIFO routines
  68. // are atomic with respect to interrupts, threads, and (most probably
  69. // even MP tasks), and are faster than the standard Mac OS equivalents
  70. // (ie Enqueue/Dequeue).
  71. //
  72. // In this sample, all the code is running at SystemTask time, however
  73. // all the list management in the critical portions of the code
  74. // (ie ProduceWidgets and ConsumeWidgets) is perfectly safe to run
  75. // at interrupt time.  [ConsumeWidgets is not interrupt safe at the
  76. // moment because it uses "printf", but you can make it interrupt safe
  77. // if your definition of "consumption" is interrupt safe.]
  78. //
  79. // Another advantage of the OT list management routines is that they
  80. // support putting elements on multiple different lists simultaneously.
  81. // For example, all widgets are on the "list of all the widgets
  82. // in the system" and one of either the free list or the pending list.
  83. // OT's list management makes this very easy to do.
  84.  
  85. /////////////////////////////////////////////////////////////////////
  86.  
  87. // The Widget data structure holds all the information for a widget
  88. // object.  This includes:
  89. //
  90. // o fSequenceNumber -- A unique, monotonically increasing, sequence
  91. //   number for each widget that is ever created.
  92. // o fCreationTime -- The time at which the widget was created.
  93. // o fLastProducedTime -- The time at which the widget was last produced.
  94. //
  95. // The data structure also holds two link fields.  The first, fNext,
  96. // is used to link together all the elements on either the pending
  97. // or free widget lists.  The second link field is used to link all
  98. // of the widgets together in one long list, regardless of what their
  99. // status is.
  100.  
  101. struct Widget {
  102.     OTLink         fNext;
  103.     OTLink         fAllWidgets;
  104.     
  105.     UInt32      fSequenceNumber;
  106.     OTTimeStamp fCreationTime;
  107.     OTTimeStamp fLastProducedTime;
  108. };
  109. typedef struct Widget Widget, *WidgetPtr;
  110.  
  111. // The following three lists are used to hold lists of widgets.
  112. // gAllWidgetList is the list of all the widgets in the system.
  113. // gPendingWidgetList is the list of all the widgets that have
  114. // been produced and are awaiting consumption.  gFreeWidgetList
  115. // is the list of all the widgets that are available for reuse
  116. // by the producer.
  117. //
  118. // A widget is always on the the gAllWidgetList (through its
  119. // fAllWidgets link) and is either on the gPendingWidgetList
  120. // or the gFreeWidgetList (through its fNext link).
  121.  
  122. static OTLIFO gAllWidgetList;
  123. static OTLIFO gPendingWidgetList;
  124. static OTLIFO gFreeWidgetList;
  125.  
  126. // gLastWidgetSequenceNumber holds the sequence number of the
  127. // last widget that was produced.  When we produce a new widget,
  128. // we add one to this number to get the sequence number for the
  129. // new widget.
  130.  
  131. static UInt32 gLastWidgetSequenceNumber;
  132.  
  133. /////////////////////////////////////////////////////////////////////
  134.  
  135. static void InitWidgetLists(void)
  136.     // Initialises all of the widget lists to empty.
  137. {
  138.     gAllWidgetList.fHead = nil;
  139.     gPendingWidgetList.fHead = nil;
  140.     gFreeWidgetList.fHead = nil;
  141.     gLastWidgetSequenceNumber = 0;
  142. }
  143.  
  144. static WidgetPtr CreateWidget(void)
  145.     // This routine creates a new widget and returns it to the
  146.     // caller.  The new widget is always added to the gAllWidgetList
  147.     // through its fAllWidgets link, but is available to be linked to
  148.     // another list through its fNext link.
  149. {
  150.     WidgetPtr result;
  151.     
  152.     // Allocate the memory for the widget.
  153.     
  154.     result = OTAllocMem(sizeof(Widget));
  155.     OTAssert("CreateWidget: Could not create widget", result != nil);
  156.     
  157.     // Fill out the information fields of the widget.  Note the
  158.     // use of OTAtomicAdd32 to increment gLastWidgetSequenceNumber
  159.     // atomically.  This guarantees that the sequence number is
  160.     // unique, even if this routine is re-entered.
  161.     
  162.     OTMemzero(result, sizeof(Widget));
  163.     result->fSequenceNumber = OTAtomicAdd32(1, (long *) &gLastWidgetSequenceNumber);
  164.     OTGetTimeStamp(&result->fCreationTime);
  165.     
  166.     // Add the widget to the list of all the widgets in the system.
  167.     
  168.     OTLIFOEnqueue(&gAllWidgetList, (OTLink *) &result->fAllWidgets);
  169.     
  170.     return (result);
  171. }
  172.  
  173. static void ProduceWidgets(UInt32 howMany)
  174.     // This routine produces howMany widgets and adds them
  175.     // to the gPendingWidgetList.
  176. {
  177.     UInt32 i;
  178.     OTLink *freeLink;
  179.     WidgetPtr thisWidget;
  180.  
  181.     // Produce each new element in turn.
  182.     
  183.     for (i = 0; i < howMany; i++) {
  184.     
  185.         // Grab a free element off the front of the gFreeWidgetList.
  186.         // If that returns nil, there is no free element and we have
  187.         // to create a new widget.  Otherwise, use OTGetLinkObject
  188.         // to derive thisWidget from freeLink.
  189.     
  190.         freeLink = OTLIFODequeue(&gFreeWidgetList);
  191.         if ( freeLink != nil ) {
  192.             thisWidget = OTGetLinkObject(freeLink, Widget, fNext);
  193.         } else {
  194.             thisWidget = CreateWidget();
  195.         }
  196.         
  197.         // At this point thisWidget points to a free widget that is
  198.         // not on the gFreeWidgetList.  We now produce the widget, which in
  199.         // this sample merely involves setting fLastProducedTime.
  200.         
  201.         OTGetTimeStamp(&thisWidget->fLastProducedTime);
  202.         
  203.         // Now add the widget to the list of produced widgets.
  204.         
  205.         OTLIFOEnqueue(&gPendingWidgetList, (OTLink *) &thisWidget->fNext);
  206.     }
  207. }
  208.  
  209. static void PrintWidget(WidgetPtr thisWidget)
  210.     // Prints a widget to stdout.
  211. {
  212.     printf("  %03d, Created @ %08x%08x, Produced @ %08x%08x",
  213.         thisWidget->fSequenceNumber,
  214.         thisWidget->fCreationTime.hi, thisWidget->fCreationTime.lo,
  215.         thisWidget->fLastProducedTime.hi, thisWidget->fLastProducedTime.lo
  216.         );
  217. }
  218.  
  219. static void ConsumeWidgets(void)
  220.     // This routine consumes all of the widgets that have been produced.
  221. {
  222.     OTLink *listToConsume;
  223.     WidgetPtr thisWidget;
  224.     
  225.     // First start by atomically stealing the list of pending
  226.     // widgets.  This removes all of the widgets on gPendingWidgetList
  227.     // and returns them to us in listToConsume.  Then, reverse
  228.     // the list so that we consume them in the same order they
  229.     // were produced.
  230.     //
  231.     // Note that these two API routines are defined to deal with
  232.     // the empty list case correctly, so we don't have to
  233.     // explicitly test it ourselves.
  234.     
  235.     listToConsume = OTLIFOStealList(&gPendingWidgetList);
  236.     listToConsume = OTReverseList(listToConsume);
  237.     
  238.     while ( listToConsume != nil ) {
  239.     
  240.         // Given the link element, derive the actual widget object.
  241.         
  242.         thisWidget = OTGetLinkObject(listToConsume, Widget, fNext);
  243.         
  244.         // Now consume the widget.  In this sample, consuming
  245.         // a widget merely involves printing it to stdout.
  246.         
  247.         PrintWidget(thisWidget);
  248.         printf("\n");
  249.         
  250.         // Move along to the next list element...
  251.         
  252.         listToConsume = listToConsume->fNext;
  253.         
  254.         // ... and enqueue the most recently consumed widget on
  255.         // the list of free widgets.  Note that the order of these
  256.         // two operations is important, because thisWidget->fNext
  257.         // is the same memory location as listToConsume->fNext, so
  258.         // we can't change thisWidget->fNext (by enqueuing it)
  259.         // until we have extracted the linkage information from it.
  260.  
  261.         OTLIFOEnqueue(&gFreeWidgetList, (OTLink *) &thisWidget->fNext);
  262.     }
  263. }
  264.  
  265. static void DumpWidgetList(OTLIFO *list)
  266.     // Dump a widget list that is linked using the fNext field.
  267.     // This is appropriate for the pending and free lists of widgets.
  268. {
  269.     OTLink *link;
  270.     WidgetPtr thisWidget;
  271.     
  272.     link = list->fHead;
  273.     while ( link != nil ) {
  274.         thisWidget = OTGetLinkObject(link, Widget, fNext);
  275.         PrintWidget(thisWidget);
  276.         printf("\n");
  277.         link = link->fNext;
  278.     }
  279. }
  280.  
  281. static void DumpWidgetAllLists(void)
  282.     // Dump each af the three global lists.
  283. {
  284.     OTLink *link;
  285.     WidgetPtr thisWidget;
  286.  
  287.     printf("gPendingWidgetList\n");
  288.     DumpWidgetList(&gPendingWidgetList);
  289.  
  290.     printf("gFreeWidgetList\n");
  291.     DumpWidgetList(&gFreeWidgetList);
  292.  
  293.     printf("gAllWidgetList\n");
  294.     
  295.     link = gAllWidgetList.fHead;
  296.     while ( link != nil ) {
  297.         thisWidget = OTGetLinkObject(link, Widget, fAllWidgets);
  298.         PrintWidget(thisWidget);
  299.         printf("\n");
  300.         link = link->fNext;
  301.     }
  302. }
  303.  
  304. /////////////////////////////////////////////////////////////////////
  305.  
  306. void main(void)
  307.     // A simple command line shell for testing the various
  308.     // routines defined above.
  309. {
  310.     OSStatus err;
  311.     Boolean quitNow;
  312.     char commandString[256];
  313.     
  314.     printf("Hello Cruel World!\n");
  315.     
  316.     err = InitOpenTransport();
  317.     
  318.     if (err == noErr) {
  319.     
  320.         InitWidgetLists();
  321.     
  322.         printf("Enter a command:\n");
  323.         printf("p) Produce 3 widgets\n");
  324.         printf("P) Produce 5 widgets\n");
  325.         printf("c) Consume all pending widgets\n");
  326.         printf("d) Dump all widget lists\n");
  327.         printf("q) Quit\n");
  328.         printf("\n");
  329.         
  330.         quitNow = false;
  331.         do {
  332.             printf("Enter a command:\n");
  333.             gets(commandString);
  334.             switch( commandString[0] ) {
  335.                 case 'q':
  336.                 case 'Q':
  337.                     quitNow = true;
  338.                     break;
  339.                     
  340.                 case 'c':
  341.                 case 'C':
  342.                     ConsumeWidgets();
  343.                     break;
  344.                     
  345.                 case 'p':
  346.                     ProduceWidgets(3);
  347.                     break;
  348.                 case 'P':
  349.                     ProduceWidgets(5);
  350.                     break;
  351.                     
  352.                 case 'd':
  353.                 case 'D':
  354.                     DumpWidgetAllLists();
  355.                     break;
  356.                     
  357.                 default:
  358.                     printf("Don't understand “%s”.");
  359.                     break;
  360.             }
  361.         } while ( ! quitNow );
  362.         
  363.         CloseOpenTransport();
  364.     }
  365.     
  366.     if (err == noErr) {
  367.         printf("Success.\n");
  368.     } else {
  369.         printf("Failed with error %d.\n", err);
  370.     }
  371.     printf("Done.  Press command-Q to Quit.\n");
  372. }
  373.  
  374.  
  375.